package ru.spb.osll.myfit.server.execute;

import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.Set;
import java.util.Map;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import ru.spb.osll.myfit.client.service.PageExecutingService;
import ru.spb.osll.myfit.common.PageExecutingResult;
import ru.spb.osll.myfit.common.PageExecutingResult.PageExecutingState;
import ru.spb.osll.myfit.common.ProgressCounts;
import ru.spb.osll.myfit.server.Configurator;
import ru.spb.osll.myfit.server.parser.TableParser;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import org.apache.log4j.Logger;

import ru.spb.osll.myfit.util.FileHelper;
import ru.spb.osll.myfit.util.StringUtils;

public class PageExecutingServiceImpl extends RemoteServiceServlet implements
    PageExecutingService {
  private Logger log = Logger.getLogger(PageExecutingServiceImpl.class);

  private static final long serialVersionUID = 3889574505512661341L;

  private HashMap<String, PageExecutor> pageExecutors = new HashMap<String, PageExecutor>();

  private Configurator m_configurator;
  
  private FileHelper m_helper;

  protected PageExecutorFactory m_pageExecutorFactory;

  protected ExecutorService m_executorService;

  private Pattern processedPattern = Pattern.compile("processed:\\s+(\\d*)");
  private Pattern rightPattern = Pattern.compile("right:\\s+(\\d*)");
  private Pattern wrongPattern = Pattern.compile("wrong:\\s+(\\d*)");
  private Pattern ignoredPattern = Pattern.compile("ignored:\\s+(\\d*)");
  private Pattern exceptionsPattern = Pattern.compile("exceptions:\\s+(\\d*)");

  public PageExecutingServiceImpl() {
    createParams();
  }

  protected void createParams() {
    m_configurator = Configurator.getInstance();
    m_helper = new FileHelper();
    m_pageExecutorFactory = new PageExecutorFactory(m_configurator, new FileHelper());
    m_executorService = Executors.newCachedThreadPool();
  }

  public PageExecutingResult getExecutingResult(String id) {
    PageExecutor executor;
    synchronized(pageExecutors) {
      executor = pageExecutors.get(id);
    }

    if (executor == null) return getResultFromFs(id);

    PageExecutingResult result = executor.getPageExecutingResult();
    if (result.getState() == PageExecutingState.FINISH ||
        result.getState() == PageExecutingState.FAIL) {
      synchronized(pageExecutors) {
        pageExecutors.remove(id);
      }
    }
    return result;
  }

  public PageExecutingResult executePage(final String pagePath) {

    try {
      PageExecutor pageExecutor = m_pageExecutorFactory
          .createPageExecutor(pagePath);
      m_executorService.execute(pageExecutor);

      PageExecutingResult result = pageExecutor.getPageExecutingResult();

      if (result.getState() != PageExecutingState.FINISH &&
          result.getState() != PageExecutingState.FAIL) {
        synchronized(pageExecutors) {
          pageExecutors.put(pageExecutor.getId(), pageExecutor);
        }
      }

      return result;

    } catch (Exception e) {
      log.error("can't execute page: " + pagePath, e);
      PageExecutingResult failResult = new PageExecutingResult();
      failResult.setState(PageExecutingState.FAIL);
      return failResult;
    }

  }

  public Set<PageExecutingResult> getTestResults() {
    HashMap<String,PageExecutingResult> map = new HashMap<String,PageExecutingResult>();
    Set<PageExecutingResult> result = new HashSet<PageExecutingResult>();
    getMapTests(map);
    getFsTests(map); 

    Set<Map.Entry<String,PageExecutingResult>> set = map.entrySet();
    Iterator<Map.Entry<String,PageExecutingResult>> iterator = set.iterator();
    while (iterator.hasNext())
      result.add(iterator.next().getValue());

    return result;
  }

  private void getMapTests(HashMap<String,PageExecutingResult> map) {
    HashMap<String, PageExecutor> temp;

    synchronized(pageExecutors) {
      temp = (HashMap<String, PageExecutor>)pageExecutors.clone();
    }

    Set<Map.Entry<String, PageExecutor>> set = temp.entrySet();
    Iterator<Map.Entry<String, PageExecutor>> iterator = set.iterator();
    while (iterator.hasNext()) {
      Map.Entry<String, PageExecutor> entry = iterator.next();
      PageExecutor executor = entry.getValue();
      map.put(entry.getKey(), executor.getPageExecutingResult());
    }
  }

  private void getFsTests(HashMap<String,PageExecutingResult> map) {
    File root = new File(m_configurator.getTestResultsDirectory());
    File[] tests = m_helper.listFiles(root);
    
    for(File test : tests) {
      String name = test.getName();
      if (m_helper.containPage(test)) {
        try {
          PageExecutingResult pageResult = getResultFromFs(name);
          if (pageResult != null) map.put(name, pageResult);
        } catch (Exception e) {}
      }
    }
  }

  private PageExecutingResult getResultFromFs(String id) {
    PageExecutingResult pageResult = null;
    String html = null;
    try {
      html = StringUtils.streamToString(m_helper.openInputFile(m_configurator.getTestResultsDirectory()
					+ File.separator + id + File.separator
					+ m_helper.parseUNameGetName(id) + ".html"));
      pageResult = new PageExecutingResult(id);
      pageResult.setState(PageExecutingState.FINISH);
      pageResult.setName(m_helper.parseUNameGetName(id));
      pageResult.setStartTime(m_helper.parseUNameGetTime(id));
      pageResult.addAllExecutedTable((new TableParser()).parse(html));

      setCounts(html, pageResult);
    } catch (Exception e) {
      pageResult = null;
    }

    return pageResult;
  }

  protected void setCounts(String html, PageExecutingResult pageResult) {
    if (html != null && !html.equals("")) {
      int processed = 0;
      int right = 0;
      int wrong = 0;
      int ignored = 0;
      int exceptions = 0;

      if (html.contains("<title> processed:") &&
              html.contains("</title>")) {

        int beginIndex = html.indexOf("<title>");
        int endIndex = html.indexOf("</title>");

        String str = html.substring(beginIndex, endIndex);

        Matcher matcher = processedPattern.matcher(str);
        matcher.find();
        String processedLine = matcher.group(1).trim();
        if (!processedLine.equals("")) {
            processed = Integer.parseInt(processedLine);
        }

        matcher = rightPattern.matcher(str);
        matcher.find();
        String rightLine = matcher.group(1).trim();
        if (!rightLine.equals("")) {
          right = Integer.parseInt(rightLine);
        }

        matcher = wrongPattern.matcher(str);
        matcher.find();
        String wrongLine = matcher.group(1).trim();
        if (!wrongLine.equals("")) {
          wrong = Integer.parseInt(wrongLine);
        }

        matcher = ignoredPattern.matcher(str);
        matcher.find();
        String ignoredLine = matcher.group(1).trim();
        if (!ignoredLine.equals("")) {
          ignored = Integer.parseInt(ignoredLine);
        }

        matcher = exceptionsPattern.matcher(str);
        matcher.find();
        String exceptionsLine = matcher.group(1).trim();
        if (!exceptionsLine.equals("")) {
          exceptions = Integer.parseInt(exceptionsLine);
        }
      }

      if (pageResult != null) {
        pageResult.setCounts(new ProgressCounts(right, wrong,
              ignored, exceptions, processed, processed));
      }
    }
  }
}
